Skip to content

[optimizer] consume KVCM event streams and report online MRC metrics - #296

Open
Tyndalllll wants to merge 18 commits into
feat/optimizer-event-streamfrom
feat/optimizer-event-stream-grpc
Open

[optimizer] consume KVCM event streams and report online MRC metrics#296
Tyndalllll wants to merge 18 commits into
feat/optimizer-event-streamfrom
feat/optimizer-event-stream-grpc

Conversation

@Tyndalllll

@Tyndalllll Tyndalllll commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • discover KVCM leaders, synchronize instance configuration, and subscribe to optimizer events over gRPC streaming
  • route streamed queries through the optimizer's shared ingestion path, with keepalive, reconnect backoff, leader refresh, and strict configuration handling
  • propagate location spec names, register full-attention instances, and replay events using producer timestamps
  • report unified per-query metrics and fixed-target MRC capacity metrics for 60%, 80%, 90%, 95%, 99%, and 99.5% theoretical hit rates
  • bound KVCM replay state with a default 24-hour TTL and document the complete online subscription flow

Attribution

PR stack

Validation

  • focused optimizer service/subscriber tests passed in both open-source and internal-source modes
  • focused metrics/MRC tests passed in both source modes
  • final rebase was checked for a clean worktree, ancestry, commit count, and whitespace errors

@Tyndalllll
Tyndalllll force-pushed the feat/optimizer-event-stream-grpc branch from 991aa72 to 02c903f Compare August 18, 2026 07:10
@Tyndalllll
Tyndalllll marked this pull request as ready for review August 19, 2026 08:57

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 02c903f174

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}
auto query_scope = report_service_metrics ? metrics_collector.MakeServiceQueryScope() : ChronoScopeGuard{};
proto::optimizer::TraceQueryResponse response;
const ErrorCode ec = optimizer_service_->ExecuteTraceQuery(event, &response);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve cache-query semantics before replaying events

When KVCM serves QT_BATCH_GET or QT_REVERSE_ROLL_SW_MATCH, this path replays the event unconditionally as a full-attention prefix query. CacheManager records query_type in CacheGetEvent, but OptimizerEventPublisher::Convert does not serialize it, and auto-created optimizer groups enable prefix hashing, so independent batch keys are rolling-hashed and interpreted as one prefix chain. For workloads using these query types, hit rates and MRC values are therefore unrelated to the actual accesses; either filter the stream to compatible prefix-match events or carry the query type through the protocol and handle each form correctly.

Useful? React with 👍 / 👎.

Comment on lines +366 to +371
if (source.location_spec_groups_size() > 1) {
KVCM_LOG_WARN("ApplyKvcmConfiguration: ignore unsupported multi-group instance[%s], groups=%d",
source.instance_id().c_str(),
source.location_spec_groups_size());
unsupported_instance_ids.insert(source.instance_id());
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Handle instances with no location-spec groups

KVCM permits legacy instances to omit location_spec_groups—the field defaults to an empty vector—but this check only classifies instances with more than one group as unsupported. A zero-group instance therefore reaches OnlineOptimizerManager::RegisterInstance, which rejects it because implicit full-only registration requires exactly one group; ApplyKvcmConfiguration then fails the entire snapshot, SyncConfiguration never calls UpdateWorker, and no events are consumed even for otherwise valid instances. Treat zero-group instances as unsupported or synthesize a full group from their location_spec_infos.

Useful? React with 👍 / 👎.

Comment on lines 519 to 522
if (state->instance_group->enable_theoretical_max_cache()) {
const uint64_t max_hits = HitCurveProjector::ProjectInfinite(fact);
state->mrc_window.Record(fact);
result.max_hit_count = ClampToInt64(max_hits);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid accumulating MRC data when interval reporting is disabled

When metrics_report_interval_ms is zero or negative, OnlineOptimizerServer::Start intentionally creates no reporting thread, but every theoretical query still records into mrc_window. Because TakeMrcMetrics is then never called, its sparse map is never reset and retains every historical reuse-distance boundary, including entries for blocks later removed by the 24-hour TTL; a long-running optimizer with interval reporting disabled can therefore accumulate substantial unbounded historical metric state. Skip MRC recording in this mode or provide another bounded/reset path.

Useful? React with 👍 / 👎.

CommonResponseHeader header = 1;
repeated int64 estimated_capacity_blocks = 2; // 各档容量按 average block size 折算的 block 数
int64 size_full_only = 3; // 仅 full spec 的单 block 字节数
int64 size_full = 3; // 仅 full spec 的单 block 字节数

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the existing registration response field name

Renaming protobuf field 3 from size_full_only to size_full is wire-compatible for binary gRPC, but it breaks the public source and HTTP contracts: regenerated clients no longer have the size_full_only accessor, and this repository's JSON serializer uses preserve_proto_field_names, so /api/optimizer/registerInstance now returns size_full instead of size_full_only. Existing HTTP consumers that read the old field silently lose the block-size result; retain the protobuf name and limit the terminology rename to internal C++ state, or provide an explicit compatibility path.

Useful? React with 👍 / 👎.

Comment on lines +329 to +333
if (report_service_metrics) {
if (ec != EC_OK) {
metrics_collector.set_service_error_code_metrics(static_cast<double>(ec));
}
metrics_reporter_->ReportPerQuery(&metrics_collector);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude intentionally unsupported events from error metrics

For an instance deliberately placed in unsupported_instance_ids_ because it has multiple location-spec groups, ExecuteTraceQuery returns EC_INSTANCE_NOT_EXIST, and this block reports it as a service error before the later unsupported-instance check suppresses the log and refresh. Every access to such a known unsupported instance therefore increments service.error_counter/error QPS, potentially dominating alerts in mixed-model deployments even though the subscriber is behaving as configured. Check the unsupported set before reporting the error, or classify these drops separately.

Useful? React with 👍 / 👎.

@Tyndalllll
Tyndalllll force-pushed the feat/optimizer-event-stream-grpc branch from 02c903f to d99e00e Compare August 21, 2026 04:09

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d99e00ecff

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

KVCM_LOG_WARN("KvcmEventSubscriber: invalid leader endpoint returned by seed[%s]", endpoint.host.c_str());
continue;
}
leader_endpoint = leader.host() + ":" + std::to_string(leader.meta_rpc_port());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bracket IPv6 leader addresses before appending the port

When GetClusterInfo returns an IPv6 literal, this produces an ambiguous target such as 2001:db8::1:6381 rather than the gRPC address [2001:db8::1]:6381. Consequently, an optimizer that successfully reaches an IPv6 seed cannot connect to the discovered leader for GetConfiguration or SubscribeEvents; format IPv6 hosts with brackets before constructing the endpoint.

Useful? React with 👍 / 👎.

@Tyndalllll
Tyndalllll force-pushed the feat/optimizer-event-stream-grpc branch from d99e00e to 9d0ef1d Compare August 24, 2026 08:42

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9d0ef1dc9f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +462 to +463
const ErrorCode ec =
manager_->TraceQuery(request.instance_id(), block_keys, input_token_len, request.timestamp_ns(), result);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Account for location specs before replaying block keys

When a supported prefix query supplies per-key location_spec_names, this call discards them and replays only block_keys. KVCM treats the spec as part of a block's logical identity and uses each per-position name to select cache candidates, so accesses such as (key, F0) followed by (key, L1) can have different serving outcomes while the optimizer treats the latter as a hit on the former; the configured full-group byte charge is also inaccurate for such partial-spec accesses. This corrupts hit-rate and MRC results even without the unsupported query types noted elsewhere, so nonempty spec-qualified events should either be modeled with spec-aware identities/charges or excluded from replay.

Useful? React with 👍 / 👎.

Comment on lines +240 to +243
kmon_ctx_->mrc_metrics.reset(reporter->RegisterMetric("mrc", kmonitor::GAUGE, kmonitor::FATAL));
if (!kmon_ctx_->mrc_metrics) {
KVCM_LOG_ERROR("failed to register metric:[mrc]");
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clear KMonitor state when MRC registration fails

If KMonitor accepts the existing metrics but rejects the newly added mrc metric, this returns failure while leaving kmon_ctx_, its kmonitor pointer, and a null mrc_metrics intact. OnlineOptimizerServer::Init explicitly continues after InitKmonitor() returns false, and once a full-attention instance exists the next interval report passes its context checks and dereferences mrc_metrics in ReportInterval, crashing the optimizer instead of running with KMonitor disabled. Reset the KMonitor context on this failure path, as is already done for earlier initialization failures.

Useful? React with 👍 / 👎.

Comment on lines +320 to +324
if (source.name().empty() || source.capacity_bytes() <= 0) {
KVCM_LOG_ERROR("ApplyKvcmConfiguration: invalid KVCM instance group[%s], capacity_bytes=%ld",
source.name().c_str(),
source.capacity_bytes());
return EC_BADARGS;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Skip zero-quota groups instead of aborting synchronization

When a KVCM snapshot contains an instance group whose quota capacity is zero or negative, this rejects the entire snapshot. KVCM itself permits these capacities—InstanceGroupQuota::ValidateRequiredFields imposes no positivity constraint, and the serving/reclaimer paths interpret nonpositive capacity as a group with no available quota—while GetConfiguration emits every registered group. Thus one intentionally disabled group prevents SyncConfiguration from calling UpdateWorker, so events are not consumed for any otherwise valid positive-capacity group; classify the nonpositive group and its instances as unsupported rather than failing the complete synchronization.

Useful? React with 👍 / 👎.

Track interval hit rates, service input tokens, request latency totals, and errors for streamed queries.

Add instance-group labels to per-instance metrics and move optional KMonitor reporting behind its own reporter.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6d0fa11917

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +123 to +124
if (!SyncConfiguration(leader_endpoint)) {
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Try alternate seeds when the discovered leader is unusable

If the first reachable seed returns a stale or temporarily unreachable leader endpoint, DiscoverLeader stops at that seed and this failure returns from the refresh without consulting the remaining healthy seeds. Because every refresh repeats the same ordering, a stale first seed can prevent configuration synchronization and event consumption indefinitely even when another seed reports a usable current leader; continue discovery with the remaining seeds when SyncConfiguration fails.

Useful? React with 👍 / 👎.

Comment on lines +323 to +325
{
OptimizerCallGuard guard(&request_context, metrics_reporter_.get());
ec = optimizer_service_->ExecuteTraceQuery(event, &response);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid access-logging every streamed cache event

When event subscription is enabled, every cache access passes through this OptimizerCallGuard, whose destructor unconditionally emits KVCM_ACCESS_LOG. Unlike an optimizer RPC, this path can run at the full KVCM cache-query rate, so it generates an additional access-log record per streamed event and can make logging I/O or disk volume dominate the single stream-processing thread, causing its bounded upstream queue to drop events. Keep the metrics/timing behavior without using the RPC guard's access-log side effect.

Useful? React with 👍 / 👎.


const RequestFact fact =
state->lite_hit->ProcessRequest(normalized.block_keys, TimestampUtil::GetCurrentTimeUs() * 1000);
const RequestFact fact = state->lite_hit->ProcessRequest(normalized.block_keys, replay_timestamp_ns);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep producer-time TTL state from being advanced by wall time

When timestamped events are delayed longer than an instance's TTL, replaying the first event here creates state at the producer timestamp, but the periodic ListInstances call advances the same LiteHit state to the optimizer's current wall clock. If reporting occurs before the next queued producer-time event, it expires that state, so an event at producer time t+5 misses data inserted at t even when the TTL is 10 seconds. This defeats the producer-timestamp semantics added for queueing delays; TTL maintenance and reporting need to use a consistent event-time watermark.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5516dc3c01

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +149 to +150
std::uint64_t previous_sequence = 0;
bool has_previous_sequence = false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Track accepted sequences across batches

When a producer resends a batch after losing the success response, this validation compares sequences only within the current request because previous_sequence is reset on every call and no progress is stored for producer_id. The repeated observations are therefore enqueued again, double-counting accesses and altering LRU/MRC results despite the sequence and last_accepted_sequence contract; retain the last accepted sequence per producer and reject or skip already accepted observations before enqueueing.

Useful? React with 👍 / 👎.

Comment on lines +151 to +154
for (const auto &observation : request->observations()) {
if (observation.trace_id().empty() || observation.instance_id().empty() ||
observation.token_ids().empty() ||
(has_previous_sequence && observation.sequence() <= previous_sequence)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject negative observation timestamps before enqueueing

When an observation supplies a negative timestamp_ns, this validation accepts it and the response counts it as successfully enqueued, but OnlineOptimizerManager::TraceQuery rejects every negative timestamp with EC_BADARGS. The subscriber therefore drops the observation asynchronously after the producer has been told it was accepted; validate the timestamp here so the whole invalid batch is rejected before SendBatch.

Useful? React with 👍 / 👎.

Comment on lines +137 to +139
if (!IsAvailable()) {
status->set_code(proto::optimizer::SERVICE_NOT_READY);
status->set_message("KVCM is unavailable");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return the follower status that triggers leader discovery

When report_trace_batch reaches a stable follower after a leader change, this branch returns SERVICE_NOT_READY, while KvCacheManagerClient._make_api_request only rediscovers the leader and retries on SERVER_NOT_LEADER. The public client therefore fails this observation call even when a healthy leader is available; return SERVER_NOT_LEADER for the follower case and reserve SERVICE_NOT_READY for recovery or queue availability failures.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants